The symmetry-exceptional slopes of K=o9_30634 are (-2,1), (-1,1), and (2,1). All three have symmetry group Z/2+Z/2 and thus they can be written in three different ways as DBC. Here we determine all these three ways. Since K is strongly invertible all slopes can be written in one way as DBC branched along a link L in S3. That means for the three symmetry-exceptional fillings we need to find another 2 ways to write them as DBCs.

In [2]:
import time
import snappy
import pandas
DBChomologies = pandas.read_csv("DBChomologies_non_alternating.csv") # We load a list of all non alternating links in 
#the HTLinkExteriors together with their homologies (if the homology is cyclic) and their crossing numbers.
### REMARK: To build the list we need to run the code in the file Build_DBChomologies which takes around 60 minutes. 
#Here we just load it.


def double_branched_cover(link):
    """
    Returns the double branched cover of the link.
    """
    L=link.copy()
    for i in range(L.num_cusps()):
        L.dehn_fill((2,0),i)
    for cov in L.covers(2):
        if (2.0, 0.0) not in cov.cusp_info('filling'):
            return cov
        


def better_is_isometric_to(X,Y,index):
    """
    Returns True if X and Y are isometric.
    Returns False if X and Y have different homologies. TO DO: Use volume to rigorously distinguish X and Y.
    Returns 'unclear' if SnapPy cannot verify it.
    The higher the index the harder SnapPy tries.
    """     
    w='unclear'
    if X.homology()!=Y.homology():
        w=False
    if w=='unclear':
        for i in (0,index):
            try:
                w=X.is_isometric_to(Y)
            except RuntimeError:
                pass
            except snappy.SnapPeaFatalError:
                pass
            if w==True:
                break
            if w==False:
                w='unclear'
            X.randomize()
            Y.randomize()
            i=i+1
    return w

def possible_DBC(homologies,max_crossings=15):
    """
    Takes a list of orders of homologies and returns a list consisting of all DBC of alternating links in the HT link table with that homologies together with the link names.
    """
    DBCList=[]
    LINKS=[]
    for order in homologies:
        LINKS=LINKS+DBChomologies.loc[(DBChomologies['homology']==order) & (DBChomologies['crossings']<=max_crossings)]['knot'].tolist()
    for link in LINKS:
        L=snappy.Manifold(link)
        D=double_branched_cover(L)
        DBCList.append([D,link])
    return DBCList

### The following two functions are written by Dunfield and search for positive triangulations.

def all_positive(manifold):
    return manifold.solution_type() == 'all tetrahedra positively oriented'

def find_positive_triangulation(manifold, tries=100):
    M = manifold.copy()
    for i in range(tries):
        if all_positive(M):
            return M
        M.randomize()
    for d in M.dual_curves():
        X = M.drill(d)
        X = X.filled_triangulation()
        X.dehn_fill((1,0))
        for i in range(tries):
            if all_positive(X):
                return X
            X.randomize()

    # In the closed case, here is another trick.
    if all(not c for c in M.cusp_info('is_complete')):
        for i in range(tries):
            # Drills out a random edge
            X = M.__class__(M.filled_triangulation())
            if all_positive(X):
                return X
            M.randomize()
            
def better_find_positive_triangulation(M,tries=1):
    '''
    Search for a positive triangulation, but ignores errors.
    '''
    RandomizeCount=0
    while RandomizeCount<tries:
        try:
            X=find_positive_triangulation(M)
            return X
        except snappy.SnapPeaFatalError:
            M.randomize()
            RandomizeCount=RandomizeCount+1
    return None

def is_alternating(knot,slope,try_hard=False,index=10,tries=1,max_cro=15):
    '''
    Checks if the slope is alternating.
    '''
    K=snappy.Manifold(knot)
    K.dehn_fill(slope)
    DBC=possible_DBC([K.homology().order()],max_crossings=max_cro) 
    for D in DBC:
        w=better_is_isometric_to(D[0],K,index)
        if w==True:
            return [slope,D[1]]
    if try_hard:
        X=better_find_positive_triangulation(K,tries)
        if X is not None:
            for D in DBC:
                Y=better_find_positive_triangulation(D[0],tries)
                if Y is not None:
                    w=better_is_isometric_to(X,Y,index)
                    if w==True:
                        return [slope,D[1]]
    return False
In [2]:
K=snappy.Manifold('o9_30634')
exc_sym_slopes=[(-2,1),(-1,1),(2,1)]
for s in exc_sym_slopes:
    K.dehn_fill(s)
    print(s,K.symmetry_group())
(-2, 1) Z/2 + Z/2
(-1, 1) Z/2 + Z/2
(2, 1) Z/2 + Z/2
In [4]:
start_time = time.time()
branching_sets=[]
knot='o9_30634'
for slope in exc_sym_slopes:
    w=is_alternating(knot,slope,index=25,max_cro=15)
    if w!=False:
        branching_sets.append([knot,w[0],w[1]])
        print('We found a branching set:',knot,slope,w[1])
        
print('Total number of branching sets we have found:', len(branching_sets))
print('Total time taken: %s minutes ---' % ((time.time() - start_time)/60))
We found a branching set: o9_30634 (-1, 1) K13n2148
Total number of branching sets we have found: 1
Total time taken: 0.8800097187360127 minutes ---

Next we will search for branching sets in more general manifolds. For that we will take links in the HT link tables fill some one component of it to get a surgery diagram of a link in a manifold. Then we take the double branched cover of that link and search for a match with a symmetric filling on A.

In [5]:
DBChomologies_branching = pandas.read_csv("DBChomologies_one_filling.csv")  
### REMARK: To build the list we need to run the code in the file Build_DBChomologies which takes around 3 days. Here we just load it.


def double_branched_cover(link):
    """
    Returns the double branched covers of the link. This works also for links in a more general manifold. 
    Note that a knot in a more general manifold may have more than one double branched cover 
    (or no double branched cover at all if the knot represents a primitive element in homology).
    This function will return the complete list of all double branched covers of the link.
    """
    L=link.copy()
    for i in range(L.num_cusps()):
        if L.cusp_info(i).filling==(0.0, 0.0):
            L.dehn_fill((2,0),i)
    return [cov for cov in L.covers(2) if (2.0, 0.0) not in cov.cusp_info('filling')]

def possible_DBC_surgery_diagrams(homologies,max_crossings=15):
    """
    Reads off the possible surgery diagrams.
    """
    DBCList=[]
    LINKS=[]
    CUSPS=[]
    SLOPES_STRINGS=[]
    for order in homologies:
        LINKS=LINKS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['knot'].tolist()
        CUSPS=CUSPS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['cusp'].tolist()
        SLOPES_STRINGS=SLOPES_STRINGS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['filling'].tolist()
    SLOPES=[]
    for string in SLOPES_STRINGS:
        string_without_brackets=string[1:-1]
        SLOPES.append(tuple(map(int, string_without_brackets.split(', '))))
    for i in range(0,len(LINKS)):
        DBCList.append([LINKS[i],SLOPES[i],CUSPS[i]])
    return DBCList

def search_for_branching_set(knot,slope,try_hard=False,index=10,tries=1,max_crossings=15):
    '''
    Searchs for a surgery diagram of the rbanching set
    '''  
    K=snappy.Manifold(knot)
    K.dehn_fill(slope)
    DBC=possible_DBC_surgery_diagrams([K.homology().order()],max_crossings) 
    for DIAGRAM in DBC:
        L=snappy.Manifold(DIAGRAM[0])
        L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
        for D in double_branched_cover(L):
            w=better_is_isometric_to(D,K,index)
            if w==True:
                return [[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
    if try_hard:
        X=better_find_positive_triangulation(K,tries)
        if X is not None:
            for DIAGRAM in DBC:
                L=snappy.Manifold(DIAGRAM[0])
                L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
                for D in double_branched_cover(L):
                    Y=better_find_positive_triangulation(D,tries)
                    if Y is not None:
                        w=better_is_isometric_to(X,Y,index)
                        if w==True:
                            return [[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]                                  
    return False

def search_for_three_branching_sets(knot,slope,try_hard=False,index=10,tries=1,max_crossings=15):
    '''
    Searchs for a surgery diagram of the branching set
    '''  
    BRANCHING_SETS=[]
    K=snappy.Manifold(knot)
    K.dehn_fill(slope)
    homologies=[]
    DBC=possible_DBC_surgery_diagrams([K.homology().order()],max_crossings) 
    for DIAGRAM in DBC:
        L=snappy.Manifold(DIAGRAM[0])
        L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
        for D in double_branched_cover(L):
            w=better_is_isometric_to(D,K,index)
            if w==True:
                BRANCHING_SETS=BRANCHING_SETS+[[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
                for i in range(L.num_cusps()):
                    if L.cusp_info(i).filling==(0.0, 0.0):
                        L.dehn_fill((1,0),i)
                if L.homology().order() not in homologies:
                    homologies.append(L.homology().order())
                if len(homologies)==3:
                    return BRANCHING_SETS
    if try_hard:
        X=better_find_positive_triangulation(K,tries)
        if X is not None:
            for DIAGRAM in DBC:
                L=snappy.Manifold(DIAGRAM[0])
                L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
                for D in double_branched_cover(L):
                    Y=better_find_positive_triangulation(D,tries)
                    if Y is not None:
                        w=better_is_isometric_to(X,Y,index)
                        if w==True:
                            BRANCHING_SETS=BRANCHING_SETS+[[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
                            for i in range(L.num_cusps()):
                                if L.cusp_info(i).filling==(0.0, 0.0):
                                    L.dehn_fill((1,0),i)
                            if L.homology().order() not in homologies:
                                homologies.append(L.homology().order())
                            if len(homologies)==3:
                                return BRANCHING_SETS
    return BRANCHING_SETS
In [6]:
start_time = time.time()
knot='o9_30634'
for slope in exc_sym_slopes:
    w=search_for_three_branching_sets(knot,slope,index=3,max_crossings=15)
    if w==[]:
        w=False
    if w!=False:
        branching_sets=branching_sets+w
        print('We found a branching set:',w)
        
print('Total number of branching sets we have found:', len(branching_sets))
print('Total time taken: %s minutes ---' % ((time.time() - start_time)/60))
We found a branching set: [['o9_30634', (-2, 1), 'L12n1012', (-8, 3), 0], ['o9_30634', (-2, 1), 'L14n23888', (-2, 1), 0], ['o9_30634', (-2, 1), 'L14n24646', (-2, 1), 0]]
We found a branching set: [['o9_30634', (-1, 1), 'L10n45', (-5, 1), 0], ['o9_30634', (-1, 1), 'L10n49', (3, 4), 0], ['o9_30634', (-1, 1), 'L11n141', (-3, 1), 0], ['o9_30634', (-1, 1), 'L14a20418', (3, 1), 0], ['o9_30634', (-1, 1), 'L14a20418', (3, 1), 1]]
We found a branching set: [['o9_30634', (2, 1), 'L13n9873', (-2, 1), 1]]
Total number of branching sets we have found: 10
Total time taken: 164.701756131649 minutes ---
In [7]:
branching_sets
Out[7]:
[['o9_30634', (-1, 1), 'K13n2148'],
 ['o9_30634', (-2, 1), 'L12n1012', (-8, 3), 0],
 ['o9_30634', (-2, 1), 'L14n23888', (-2, 1), 0],
 ['o9_30634', (-2, 1), 'L14n24646', (-2, 1), 0],
 ['o9_30634', (-1, 1), 'L10n45', (-5, 1), 0],
 ['o9_30634', (-1, 1), 'L10n49', (3, 4), 0],
 ['o9_30634', (-1, 1), 'L11n141', (-3, 1), 0],
 ['o9_30634', (-1, 1), 'L14a20418', (3, 1), 0],
 ['o9_30634', (-1, 1), 'L14a20418', (3, 1), 1],
 ['o9_30634', (2, 1), 'L13n9873', (-2, 1), 1]]

Since the knot K is strongly invertible we have always at least on way to write the knot as a DBC over a link in S3. by checking that the above surgery links always have the filling knot an unknot we see that we can write the (-1,1)-and the (-2,1)-filling in two more ways as DBC over a link in a lens space. (These two ways are distinguished by the homology of the lens space. This shows that these two slopes can only be written in a single way as the DBC over a link in S3. For the (2,1)-filling we found a surgery description along a link in RP3. Next, we find the remaining description by searching through surgery descriptions along 4 component links with 2 components filled.

In [1]:
import snappy
In [2]:
import time
In [3]:
def double_branched_cover(link):
    """
    Returns the double branched covers of the link. This works also for links in a more general manifold. 
    Note that a knot in a more general manifold may have more than one double branched cover 
    (or no double branched cover at all if the knot represents a primitive element in homology).
    This function will return the complete list of all double branched covers of the link.
    """
    L=link.copy()
    for i in range(L.num_cusps()):
        if L.cusp_info(i).filling==(0.0, 0.0):
            L.dehn_fill((2,0),i)
    return [cov for cov in L.covers(2) if (2.0, 0.0) not in cov.cusp_info('filling')]
In [8]:
def better_is_isometric_to(X,Y,index=10):
    """
    Returns True if X and Y are isometric.
    Returns False if X and Y have different homologies.
     """ 
    if X.homology()!=Y.homology():
        return False
    for i in (0,index):
        try:
            w=X.is_isometric_to(Y)
            if w==True:
                return w
        except (RuntimeError,snappy.SnapPeaFatalError):
            pass
        X.randomize()
        Y.randomize()
    return False
In [13]:
start_time=time.time()

K=snappy.Manifold('K9_449(-12,1)')
slopes=[(0,1),(1,1),(-1,1),(2,1),(-2,1)]
four_cusps=[]

for L in snappy.HTLinkExteriors(knots_vs_links='links',crossings=12,alternating=False):
    if L.num_cusps()==4:
        four_cusps.append(L)

for L in four_cusps[150:158]:
        print(L)
        for i in range(4):
            for s in slopes:
                L.dehn_fill(s,i)
                for j in range(4):
                    if j!=i:
                        for r in slopes:
                            L.dehn_fill(r,j)
                            for C in double_branched_cover(L):
                                if better_is_isometric_to(K,C):
                                    print('DBC',L,'=',K)
                            L.dehn_fill((0,0),j)
                L.dehn_fill((0,0),i)
print('Seconds taken:',time.time()-start_time)
L12n2160(0,0)(0,0)(0,0)(0,0)
L12n2161(0,0)(0,0)(0,0)(0,0)
L12n2162(0,0)(0,0)(0,0)(0,0)
L12n2163(0,0)(0,0)(0,0)(0,0)
DBC L12n2163(0,0)(-2,1)(-2,1)(0,0) = K9_449(-12,1)
DBC L12n2163(0,0)(-2,1)(-2,1)(0,0) = K9_449(-12,1)
DBC L12n2163(0,0)(0,0)(-2,1)(-2,1) = K9_449(-12,1)
DBC L12n2163(0,0)(0,0)(-2,1)(-2,1) = K9_449(-12,1)
L12n2164(0,0)(0,0)(0,0)(0,0)
L12n2165(0,0)(0,0)(0,0)(0,0)
L12n2166(0,0)(0,0)(0,0)(0,0)
L12n2167(0,0)(0,0)(0,0)(0,0)
Seconds taken: 10.091275930404663

This finishes the classification of the branching sets.